package com.zmm.util.document.code.mapping;

import com.zmm.util.document.IdoGroup;
import com.zmm.util.document.IdoItem;
import com.zmm.util.document.collection.MapUtil;
import com.zmm.util.document.md.Md5Util;
import com.zmm.util.document.param.ParamHandle;
import com.zmm.util.document.register.Interface;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;

import java.lang.reflect.Field;
import java.util.Comparator;
import java.util.Map;
import java.util.Set;

/**
 * @author 900045
 * @description:
 * @name MappingHandler
 * @date By 2021-10-26 15:21:40
 */
@Component
public class MappingHandler {

	@Autowired
	private RequestMappingHandlerMapping requestMappingHandlerMapping;

	private IdoGroup group;
	private Map<String, IdoItem> itemMap;
	private Map<String, HandlerMethod>   methodMap;

	public Object api(String code) {
		if (code != null) {
			return initParam(code);
		}
		if (group != null) {
			return group;
		}
		//init
		itemMap = MapUtil.hashMap();
		methodMap = MapUtil.hashMap();
		group = new IdoGroup();
		apiList(group);
		return group;
	}



	public void apiList(IdoGroup group) {
		Map<RequestMappingInfo, HandlerMethod> map = requestMappingHandlerMapping.getHandlerMethods();
		RequestMappingInfo key = null;
		HandlerMethod value = null;
		Set<RequestMethod> methods = null;
		IdoItem item = null;
		for (Map.Entry<RequestMappingInfo, HandlerMethod> entry : map.entrySet()) {
			key = entry.getKey();
			value = entry.getValue();
			Interface methodAnnotation = value.getMethodAnnotation(Interface.class);
			if (methodAnnotation != null && methodAnnotation.isIgnore()) {
				continue;
			}
			item = new IdoItem();
			item.setPatterns(key.getPatternsCondition().getPatterns());
			item.setCode(Md5Util.generateApiCode(item.getPatterns()));
			if (methodAnnotation != null) {
				item.setName(methodAnnotation.description());
				item.setRemarks(methodAnnotation.remarks());
				item.setVersion(methodAnnotation.version());
			}
			methods = key.getMethodsCondition().getMethods();
			//请求方式
			if (!methods.isEmpty()) {
				this.parseMethods(item, methods);
			}
			itemMap.put(item.getCode(), item);
			methodMap.put(item.getCode(), value);
			group.add(item);
		}
		//排序   根据版本号
		sortApi();
	}

	public IdoItem initParam(String code) {
		if (itemMap == null) {
			api(null);
		}
		IdoItem item = itemMap.get(code);
		if (item == null) {
			return null;
		}
		HandlerMethod method = methodMap.get(code);
		item.setReqParams(null);
		item.setRespParams(null);
		ParamHandle.parseReqParams(item, method.getMethod().getParameters());
		parseRespParams(item, method);
		return item;
	}
	

	private void parseMethods(IdoItem model, Set<RequestMethod> methods) {
		for (RequestMethod method : methods) {
			model.addMethod(method.name());
		}
	}

	

	//  响应参数   ---------------------------------------------------------------------------start     
	/**
	 * 响应参数解析
	 *
	 * @param item
	 * @param method
	 */
	private void parseRespParams(IdoItem item, HandlerMethod method) {
		Interface methodAnnotation = method.getMethodAnnotation(Interface.class);
		if (methodAnnotation == null || methodAnnotation.isIgnore()) {
			return;
		}
		if (methodAnnotation.respClass() == Object.class) {
			return;
		}
		Field[] fields = methodAnnotation.respClass().getDeclaredFields();
		ParamHandle.parseParamAnnotation(item, fields);
	}
	

	//  响应参数   ---------------------------------------------------------------------------end

	/**
	 * 根据版本号排序
	 *
	 *    1.6.0 > 1.5 > 1.5.0
	 */
	private void sortApi() {
		if (group == null || group.getItems() == null) {
			return;
		}
		group.getItems().sort(new Comparator<IdoItem>() {

			@Override
			public int compare(IdoItem o1, IdoItem o2) {
				String version1 = o1.getVersion();
				String version2 = o2.getVersion();
				if (StringUtils.isBlank(version1) && StringUtils.isBlank(version2)) {
					return 0;
				}
				if (!StringUtils.isBlank(version1) && StringUtils.isBlank(version2)) {
					return -1;
				}
				if (StringUtils.isBlank(version1) && !StringUtils.isBlank(version2)) {
					return 1;
				}
				String[] v1 = version1.split("\\.");
				String[] v2 = version2.split("\\.");
				int maxLen = v1.length > v2.length ? v1.length : v2.length;
				int node1 = 0;
				int node2 = 0;
				for (int i = 0; i < maxLen; i++) {

					if (v1.length < (i + 1)) {
						return -1;
					}
					if (v2.length < (i + 1)) {
						return 1;
					}
					node1 = Integer.parseInt(v1[i]);
					node2 = Integer.parseInt(v2[i]);
					if (node1 != node2) {
						return node2 - node1;
					}
				}
				return 0;
			}
		});

	}
}
